﻿using System;
using CodingWheels.DataTypes.Core;

namespace CodingWheels.DataTypes
{
	public class AngleUnit : Unit
	{
		public static readonly AngleUnit Degrees = new AngleUnit("Degrees", "°");
		public static readonly AngleUnit Radians = new AngleUnit("Radians", "rad");

		private AngleUnit(string name, string symbol) : base(name, symbol)
		{
		}
	}

	public class Angle : DataType<Angle, AngleUnit>
	{
		public Angle() : base(0, AngleUnit.Degrees)
		{
		}

		public Angle(double value, AngleUnit unit) : base(value, unit)
		{
		}

		public Angle Sine()
		{
			return Math.Sin(this);
		}

		public Angle ArcSine()
		{
			return Math.Asin(this);
		}

		public Angle Cosine()
		{
			return Math.Cos(this);
		}

		public Angle ArcCosine()
		{
			return Math.Acos(this);
		}

		public Angle Tangent()
		{
			return Math.Tan(this);
		}

		public Angle ArcTangent()
		{
			return Math.Atan(this);
		}

		public Angle ArcTangent2(Angle otherAngle)
		{
			return Math.Atan2(this, otherAngle);
		}

		///<summary>
		/// Implicitly converts and <see cref="Angle"/> to a double representing the 
		/// angle in radians
		///</summary>
		///<param name="angle">Angle to be converted.</param>
		///<returns>A double containing the <paramref name="angle"/> in radians.</returns>
		public static implicit operator double(Angle angle)
		{
			return angle.In(AngleUnit.Radians);
		}

		/// <summary>
		/// Performs an implicit conversion from <see cref="System.Double"/> to <see cref="Angle"/>.
		/// </summary>
		/// <param name="radians">The radians.</param>
		/// <returns>The result of the conversion.</returns>
		public static implicit operator Angle(double radians)
		{
			return new Angle(radians, AngleUnit.Radians);
		}
	}

	internal class AngleConverter : Converter<AngleUnit>
	{
		public AngleConverter()
		{
			AddConversion(Conversion<AngleUnit>.ConvertUsingMultiplication(AngleUnit.Degrees, AngleUnit.Radians, (Math.PI / 180.0)));
		}
	}
}
